package com.cloud.common.redislock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

public class RedisLock {
    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
    private static final Long SUCCESS = 1L;

    private RedisTemplate<String,Object> redisTemplate;
    //锁key的前缀
    private final static String prefix_key = "redisLock:";


    public RedisLock(RedisTemplate<String,Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    public boolean lock(LockParam lockParam) {
        Long tryLockEndTime=System.currentTimeMillis()+lockParam.getTryLockTime();
        while(true){
            //判断是否超过了，尝试获取锁的时间
            if(System.currentTimeMillis()>tryLockEndTime){
                return false;
            }
            //尝试获取锁
            String value=prefix_key.concat(lockParam.getLockValue());
            Boolean result = tryLock(lockParam.getLockKey(),value,lockParam.getHoldLockTime());
            if(result){
                return true;//获取锁成功
            }

            try {
                //获得锁失败，休眠50毫秒再去尝试获得锁,避免一直请求redis，导致redis cpu飙升
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public boolean unLock(LockParam lockParam) {
        return unLock(lockParam.getLockKey(),lockParam.getLockValue());
    }
    /**
     * 尝试加锁
     * @param lockKey   锁的key
     * @param value     锁的val
     * @param expireTime    超时时间(秒)
     * @return
     */
    public boolean tryLock(String lockKey, String value, int expireTime) {
        System.out.println(expireTime);
        try {
            String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value, expireTime);
            if (SUCCESS.equals(result)) {
                return true;
            }

        } catch (Exception e) {
            log.error("分布式锁加锁失败", e);
        }
        return false;
    }

    /**
     * 解锁
     * @param lockKey
     * @param value
     * @return
     */
    public boolean unLock(String lockKey, String value) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
        Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value);
        if (SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

}
